
/**
 * Copyright 2009 cestum.org
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
 
package org.waveprotocol.wave.model.document.operation.algorithm {
import org.waveprotocol.wave.model.document.operation.AnnotationBoundaryMap;
import org.waveprotocol.wave.model.document.operation.Attributes;
import org.waveprotocol.wave.model.document.operation.AttributesUpdate;
import org.waveprotocol.wave.model.document.operation.BufferedDocOp;
import org.waveprotocol.wave.model.document.operation.DocumentOperation;
import org.waveprotocol.wave.model.document.operation.EvaluatingDocOpCursor;
import org.waveprotocol.wave.model.document.operation.impl.AttributesUpdateImpl;
import org.waveprotocol.wave.model.document.operation.impl.DocOpBuffer;

/**
 * A reverser of document operations.
 *
 */
public final class DocOpInverter  implements EvaluatingDocOpCursor {

  private var target:EvaluatingDocOpCursor;

  public function DocOpInverter(target:EvaluatingDocOpCursor) {
    this.target = target;
  }

  
  public function finish():BufferedDocOp {
    return target.finish();
  }

  
  public function retain(itemCount:int):void {
    target.retain(itemCount);
  }

  
  public function characters(chars:String):void {
    target.deleteCharacters(chars);
  }

  
  public function elementStart(type:String, attrs:Attributes):void {
    target.deleteElementStart(type, attrs);
  }

  
  public function elementEnd():void {
    target.deleteElementEnd();
  }

  
  public function deleteCharacters(chars:String):void {
    target.characters(chars);
  }

  
  public function deleteElementStart(type:String, attrs:Attributes):void {
    target.elementStart(type, attrs);
  }

  
  public function deleteElementEnd():void {
    target.elementEnd();
  }

  
  public function replaceAttributes(oldAttrs:Attributes, newAttrs:Attributes):void {
    target.replaceAttributes(newAttrs, oldAttrs);
  }

  
  public function updateAttributes(attrUpdate:AttributesUpdate):void {
    var update:AttributesUpdate= new AttributesUpdateImpl(null);
    // TODO: This is a little silly. We should do this a better way.
    for (var i:int= 0; i < attrUpdate.changeSize(); ++i) {
      update = update.composeWith(AttributesUpdateImpl.of(attrUpdate.getChangeKey(i),
          attrUpdate.getNewValue(i), attrUpdate.getOldValue(i)));
    }
    target.updateAttributes(update);
  }

  
  public function annotationBoundary( map:AnnotationBoundaryMap):void {
    // Warning: Performing multiple reversals can cause multiple wrappers to be created.
    // TODO: Maybe we should change this so that this issue doesn't occur.
    target.annotationBoundary(new InverterAnnotationBoundaryMap(map));
  }

  public static function invert(input:DocumentOperation):BufferedDocOp {
    var inverter:DocOpInverter= new DocOpInverter(new DocOpBuffer());
    input.apply(inverter);
    return BufferedDocOp(inverter.finish());
  }

}
}